import * as THREE from '../../build/three.module.js';
import Stats from '../../jsm/libs/stats.module.js';

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050505);

const camera = new THREE.PerspectiveCamera(65, innerWidth / innerHeight, 1, 10000);

const bGmt = new THREE.BoxBufferGeometry(2, 2, 2);
const gmt = new THREE.InstancedBufferGeometry();
gmt.index = bGmt.index;
gmt.attributes = bGmt.attributes;

const instanceCount = 5000,
      offsets = [],
      orientations = [],
      vector = new THREE.Vector4(),
      moveQ = new THREE.Quaternion( 0.5, 0.5, 0.5, 0.0 ).normalize(),
		  tmpQ = new THREE.Quaternion(),
		  currentQ = new THREE.Quaternion();

let x, y, z, w;

for(let i = 0; i < instanceCount; i += 1) {
  // 偏移量
  x = Math.random() * 100 - 50;
  y = Math.random() * 100 - 50;
  z = Math.random() * 100 - 50;

  vector.set(x, y, z, 0).normalize();
  vector.multiplyScalar(5); // 放大5倍

  offsets.push(x + vector.x, y + vector.y, z + vector.z);

  // 方向
  x = Math.random() * 2 - 1;
  y = Math.random() * 2 - 1;
  z = Math.random() * 2 - 1;
  w = Math.random() * 2 - 1;
  vector.set(x, y, z, w).normalize();

  orientations.push(vector.x, vector.y, vector.z, vector.w)
}

gmt.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3));
gmt.setAttribute('orientation', new THREE.InstancedBufferAttribute(new Float32Array(orientations), 4).setUsage(THREE.DynamicDrawUsage));

const mtl = new THREE.ShaderMaterial({
  uniforms: {
    map: { value: new THREE.TextureLoader().load('../../textures/crate.gif') }
  },
  vertexShader: document.getElementById('vertexShader').textContent,
  fragmentShader: document.getElementById('fragmentShader').textContent
})

const mesh = new THREE.Mesh(gmt, mtl);
scene.add(mesh);

const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const stats = new Stats();
document.body.appendChild(stats.dom);

let lastTime = 0;
const render = () => {
  const time = performance.now();
  mesh.rotation.y = time * 0.00005;

  const delta = (time - lastTime) / 5000;
  tmpQ.set(moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1).normalize();

  for(let i = 0; i < gmt.attributes.orientation.count; i += 1) {
    currentQ.fromArray(gmt.attributes.orientation.array, (i * 4));
    currentQ.multiply(tmpQ);

    gmt.attributes.orientation.setXYZW(i, currentQ.x, currentQ.y, currentQ.z, currentQ.w);
  }

  gmt.attributes.orientation.needsUpdate = true;
  lastTime = time;

  renderer.render(scene, camera);
}

const animate = () => {
  requestAnimationFrame(animate);

  stats.update();
  render();
}
animate();

const handleWindowResize = () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
}
window.addEventListener('resize', handleWindowResize, false);
